package QianFeng11.ReflectionDemo;

import org.junit.Test;

import java.lang.reflect.*;

/**
 * 反射常用的方法：
 * 获取包名 类名
 * clazz.getPackage().getName()//包名
 * clazz.getSimpleName()//类名
 * clazz.getName()//完整类名
 *
 * 获取成员变量定义信息
 * getFields()//获取所有公开的成员变量,包括继承变量
 * getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
 * getField(变量名)
 * getDeclaredField(变量名)
 *
 * 获取构造方法定义信息
 * getConstructor(参数类型列表)//获取公开的构造方法
 * getConstructors()//获取所有的公开的构造方法
 * getDeclaredConstructors()//获取所有的构造方法,包括私有
 * getDeclaredConstructor(int.class,String.class)
 *
 * 获取方法定义信息
 * getMethods()//获取所有可见的方法,包括继承的方法
 * getMethod(方法名,参数类型列表)
 * getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
 * getDeclaredMethod(方法名,int.class,String.class)
 *
 * 反射新建实例
 * clazz.newInstance();//执行无参构造创建对象
 * clazz.newInstance(666,”海绵宝宝”);//执行含参构造创建对象
 * clazz.getConstructor(int.class,String.class)//获取构造方法
 *
 * 反射调用成员变量
 * clazz.getDeclaredField(变量名);//获取变量
 * clazz.setAccessible(true);//使私有成员允许访问
 * f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
 * f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null
 *
 * 反射调用成员方法
 * Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
 * m.setAccessible(true);//使私有方法允许被调用
 * m.invoke(实例,参数数据);//让指定实例来执行该方法
 * ---------------------------------------------------------------------------------------------
 * ----------------------------------------------------------------------------------------------
 * Class类
 * Class类是一切的反射根源
 *
 * 得到Class类的对象有三种方式:
 * 第一种形式: Object类 中的getClass()方法
 * 第二种形式: 类.class
 * 第三种形式: 通过Class类的forName方法
 *
 * 使用Class类进行对象的实例化操作
 * 调用无参构造进行实例化
 *      public T newInstance() throws InstantiationException,IllegalAccessException
 * 调用有参构造进行实例化
 *      public Constructor<?>[] getConstructors() throws SecurityException
 **/
public class ReflectionDemo {

    /**
     * 获取Class类的对象有三种方式
     **/
    @Test //利用导入的Junit测试包，可以对代码进行测试运行
    public void test1() {
        //通过对象的getClass()方法
        Dog dog = new Dog("小明", 4, "白色");
        Class aClass = dog.getClass();

        //通过类.Class
        Class dogClass = Dog.class;

        //通过Class.forName方法
        try {
            Class aClass2 = Class.forName("Dog");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    /**
     * 通过反射来实例化对象
     * 调用无参构造进行实例化
     *      public T newInstance() throws InstantiationException,IllegalAccessException
     * 调用有参构造进行实例化
     *      public Constructor<?>[] getConstructors() throws SecurityException
     **/
    @Test
    public void test2() {
        Class<Dog> dogClass = Dog.class;
        try {
            //通过Class对象实例化对象，调用了默认无参数的构造方法
            Dog dog = (Dog) dogClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        //获取所有的构造方法
        Class<Dog> dogClass1 = Dog.class;
        Constructor<?>[] constructors = dogClass1.getConstructors();
        for (int i = 0; i < constructors.length; i++) {
            System.out.println(constructors[i].getName());
            System.out.println(constructors[i].getParameterCount());//构造方法中参数的数量
            System.out.println(constructors[i].getModifiers());
        }

        try {
            //获取一个指定的构造方法
            Constructor<Dog> constructor = dogClass1.getConstructor(String.class, int.class, String.class);
            //调用带参数的构造器来实例化对象
            constructor.newInstance("小白", 5, "白色");
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取所有的属性
     * 取得一个类中的全部属性
     * public Field[] getFields()
     * public Field[] getDeclaredFields()
     * public Class<?> getType()
     * public int getModifiers()
     * public String getName()
     *
     * 直接调用属性
     * 取得属性
     * public Object get(Object obj)
     * //设置属性，等同于使用 “=” 完成操作
     * public void set(Object obj,Object value)
     * //让属性对外部可见
     * public void setAccessible(boolean flag)
     **/
    @Test
    public void Test3() {
        Class<Dog> dogClass = Dog.class;
        //获得属性（非私有）
        Field[] fields = dogClass.getFields();
        System.out.println(fields.length);
        //获取全部的属性（包括私有）
        Field[] declaredFields = dogClass.getDeclaredFields();
        System.out.println(declaredFields.length);
        for (Field declaredField : declaredFields) {
            int modifiers = declaredField.getModifiers();
            System.out.println(Modifier.toString(modifiers)
                    + " " + declaredField.getType()
                    + " "  + declaredField.getName()
            );
        }
    }

    /**
     * 通过Class类取得类的信息
     * 取得类所在的包
     * public Package getPackage() //得到一个类所在的包
     * public String getName() //得到名字
     * 取得一个类中的全部方法
     * public Method[] getMethods()
     * public int getModifiers() //Modifier.toString(mod);//还原修饰符
     * public Class<?> getReturnType()
     * public Class< ?>[] getParameter Types()
     * public Class< ?>[] getExceptionTypes()
     * public static String toString(int mod)
     *
     * 调用类中的方法
     * 调用类中的方法，传入实例化对象，以及具体的参数内容
     * public Object invoke(Object obj,Object... args)
     *
     * //去除访问修饰符的检查，可以调用私有的属性方法
     * public void setAccessible(boolean flag)
     **/
    @Test
    public void Test4() {
        Dog dog = new Dog("张三", 4, "Red");
        Class<Dog> dogClass = Dog.class;
        //获取类的包名
        Package aPackage = dogClass.getPackage();
        System.out.println(aPackage.getName());

        //获取公共的方法，包括继承的公有方法(Object相关方法)
        Method[] methods = dogClass.getMethods();
        for (int i = 0; i < methods.length; i++) {
            System.out.println(methods[i]);
            if (methods[i].getName().equals("toString")) {
                try {
                    String s =(String) methods[i].invoke(dog);
                    System.out.println(s);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        //访问私有方法，获取本类中定义的所有方法（不包括父类Object方法）
        System.out.println("---------------------------private---------------------------");
        Method[] declaredMethods = dogClass.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            System.out.println(declaredMethods[i]);
            if (declaredMethods[i].getName().equals("set")) {
                //设置私有方法可以被访问（去除访问修饰符的检查）
                declaredMethods[i].setAccessible(true);
                try {
                    String s2 = (String) declaredMethods[i].invoke(dog);
                    System.out.println(s2);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
